{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3xW4O0zuKfWD"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from openai import OpenAI, pydantic_function_tool\n",
        "import rich\n",
        "import requests\n",
        "import json\n",
        "from pydantic import BaseModel, Field\n",
        "from google.colab import userdata"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "api_key = userdata.get('OPENAI_API_KEY')\n",
        "MODEL = \"gpt-4o-mini\"\n",
        "\n",
        "openai = OpenAI(api_key=api_key)"
      ],
      "metadata": {
        "id": "vUydnLeLKs03"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Using Pydantic generated function structure to send in Chat API and Responses API**"
      ],
      "metadata": {
        "id": "blCx6gu1Vl46"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "The Pydantic-generated function structure is acceptable in OpenAI's Chat API, but the Responses API requires a slightly different structure."
      ],
      "metadata": {
        "id": "nznKPApuXC9z"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class GetWeather(BaseModel):\n",
        "    latitude: float = Field(..., description=\"Latitude of the location\")\n",
        "    longitude: float = Field(..., description=\"Longitude of the location\")\n",
        "\n",
        "def get_weather(latitude, longitude):\n",
        "    response = requests.get(f\"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m\")\n",
        "    data = response.json()\n",
        "    print(f\"get_weather function called to get weather for latitude = {latitude}, longitude = {longitude}\")\n",
        "    print(f\"And result is  = {data['current']['temperature_2m']}\")\n",
        "    return data['current']['temperature_2m']\n",
        "\n",
        "# Notice the function property in the output, which is not acceptable in Responses API\n",
        "rich.print(pydantic_function_tool(GetWeather))"
      ],
      "metadata": {
        "id": "qvJhp-k5XEmW",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 289
        },
        "outputId": "2dc0d458-5b3a-4352-e837-6eb90c71bdbc"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m{\u001b[0m\n",
              "    \u001b[32m'type'\u001b[0m: \u001b[32m'function'\u001b[0m,\n",
              "    \u001b[32m'function'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "        \u001b[32m'name'\u001b[0m: \u001b[32m'GetWeather'\u001b[0m,\n",
              "        \u001b[32m'strict'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n",
              "        \u001b[32m'parameters'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "            \u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "                \u001b[32m'latitude'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Latitude of the location'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Latitude'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'number'\u001b[0m\u001b[1m}\u001b[0m,\n",
              "                \u001b[32m'longitude'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Longitude of the location'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Longitude'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'number'\u001b[0m\u001b[1m}\u001b[0m\n",
              "            \u001b[1m}\u001b[0m,\n",
              "            \u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'latitude'\u001b[0m, \u001b[32m'longitude'\u001b[0m\u001b[1m]\u001b[0m,\n",
              "            \u001b[32m'title'\u001b[0m: \u001b[32m'GetWeather'\u001b[0m,\n",
              "            \u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
              "            \u001b[32m'additionalProperties'\u001b[0m: \u001b[3;91mFalse\u001b[0m\n",
              "        \u001b[1m}\u001b[0m\n",
              "    \u001b[1m}\u001b[0m\n",
              "\u001b[1m}\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>,\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'strict'</span>: <span style=\"color: #00ff00; text-decoration-color: #00ff00; font-style: italic\">True</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'parameters'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'latitude'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Latitude of the location'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Latitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'number'</span><span style=\"font-weight: bold\">}</span>,\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'longitude'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Longitude of the location'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Longitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'number'</span><span style=\"font-weight: bold\">}</span>\n",
              "            <span style=\"font-weight: bold\">}</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'latitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'longitude'</span><span style=\"font-weight: bold\">]</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'additionalProperties'</span>: <span style=\"color: #ff0000; text-decoration-color: #ff0000; font-style: italic\">False</span>\n",
              "        <span style=\"font-weight: bold\">}</span>\n",
              "    <span style=\"font-weight: bold\">}</span>\n",
              "<span style=\"font-weight: bold\">}</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Chat Completion API\n",
        "\n",
        "https://platform.openai.com/docs/guides/function-calling?api-mode=chat"
      ],
      "metadata": {
        "id": "5wlqKfVBKyjD"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "First Step where model will responed with tool call request"
      ],
      "metadata": {
        "id": "EgXT84U9Tfqr"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "messages=[\n",
        "    {\"role\": \"developer\", \"content\": \"You are a helpful assistant and provide update on weather in a city.\"},\n",
        "    # {\"role\": \"user\", \"content\": \"What's the weather like in Karachi, Pakistan?\"}\n",
        "    # {\"role\": \"user\", \"content\": \"NYC\"}\n",
        "    {\"role\": \"user\", \"content\": \"Berlin\"}\n",
        "]\n",
        "# Except for this line, everything else is the same as in the previous example\n",
        "tools = [pydantic_function_tool(GetWeather)]\n",
        "response = openai.chat.completions.create(\n",
        "    model=MODEL,\n",
        "    messages=messages,\n",
        "    tools = tools\n",
        ")\n",
        "\n",
        "rich.print(response.choices[0])\n",
        "print(\"Finish Reason = \", response.choices[0].finish_reason)\n",
        "rich.print(response.choices[0].message.tool_calls)\n",
        "# rich.print(response)\n"
      ],
      "metadata": {
        "id": "dBrUkMCUKz0s",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 466
        },
        "outputId": "784c6787-a1e1-4a69-f916-2571c474ca1a"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1;35mChoice\u001b[0m\u001b[1m(\u001b[0m\n",
              "    \u001b[33mfinish_reason\u001b[0m=\u001b[32m'tool_calls'\u001b[0m,\n",
              "    \u001b[33mindex\u001b[0m=\u001b[1;36m0\u001b[0m,\n",
              "    \u001b[33mlogprobs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
              "    \u001b[33mmessage\u001b[0m=\u001b[1;35mChatCompletionMessage\u001b[0m\u001b[1m(\u001b[0m\n",
              "        \u001b[33mcontent\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
              "        \u001b[33mrefusal\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
              "        \u001b[33mrole\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n",
              "        \u001b[33mannotations\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n",
              "        \u001b[33maudio\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
              "        \u001b[33mfunction_call\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
              "        \u001b[33mtool_calls\u001b[0m=\u001b[1m[\u001b[0m\n",
              "            \u001b[1;35mChatCompletionMessageToolCall\u001b[0m\u001b[1m(\u001b[0m\n",
              "                \u001b[33mid\u001b[0m=\u001b[32m'call_11LNwPCU7WRffzUueeDePhun'\u001b[0m,\n",
              "                \u001b[33mfunction\u001b[0m=\u001b[1;35mFunction\u001b[0m\u001b[1m(\u001b[0m\u001b[33marguments\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"latitude\":52.52,\"longitude\":13.405\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'GetWeather'\u001b[0m\u001b[1m)\u001b[0m,\n",
              "                \u001b[33mtype\u001b[0m=\u001b[32m'function'\u001b[0m\n",
              "            \u001b[1m)\u001b[0m\n",
              "        \u001b[1m]\u001b[0m\n",
              "    \u001b[1m)\u001b[0m\n",
              "\u001b[1m)\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">Choice</span><span style=\"font-weight: bold\">(</span>\n",
              "    <span style=\"color: #808000; text-decoration-color: #808000\">finish_reason</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'tool_calls'</span>,\n",
              "    <span style=\"color: #808000; text-decoration-color: #808000\">index</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span>,\n",
              "    <span style=\"color: #808000; text-decoration-color: #808000\">logprobs</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
              "    <span style=\"color: #808000; text-decoration-color: #808000\">message</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ChatCompletionMessage</span><span style=\"font-weight: bold\">(</span>\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">content</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">refusal</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">role</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'assistant'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">annotations</span>=<span style=\"font-weight: bold\">[]</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">audio</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">function_call</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-style: italic\">None</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">tool_calls</span>=<span style=\"font-weight: bold\">[</span>\n",
              "            <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ChatCompletionMessageToolCall</span><span style=\"font-weight: bold\">(</span>\n",
              "                <span style=\"color: #808000; text-decoration-color: #808000\">id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'call_11LNwPCU7WRffzUueeDePhun'</span>,\n",
              "                <span style=\"color: #808000; text-decoration-color: #808000\">function</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">Function</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">arguments</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'{\"latitude\":52.52,\"longitude\":13.405}'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span><span style=\"font-weight: bold\">)</span>,\n",
              "                <span style=\"color: #808000; text-decoration-color: #808000\">type</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>\n",
              "            <span style=\"font-weight: bold\">)</span>\n",
              "        <span style=\"font-weight: bold\">]</span>\n",
              "    <span style=\"font-weight: bold\">)</span>\n",
              "<span style=\"font-weight: bold\">)</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Finish Reason =  tool_calls\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m[\u001b[0m\n",
              "    \u001b[1;35mChatCompletionMessageToolCall\u001b[0m\u001b[1m(\u001b[0m\n",
              "        \u001b[33mid\u001b[0m=\u001b[32m'call_11LNwPCU7WRffzUueeDePhun'\u001b[0m,\n",
              "        \u001b[33mfunction\u001b[0m=\u001b[1;35mFunction\u001b[0m\u001b[1m(\u001b[0m\u001b[33marguments\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"latitude\":52.52,\"longitude\":13.405\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'GetWeather'\u001b[0m\u001b[1m)\u001b[0m,\n",
              "        \u001b[33mtype\u001b[0m=\u001b[32m'function'\u001b[0m\n",
              "    \u001b[1m)\u001b[0m\n",
              "\u001b[1m]\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">[</span>\n",
              "    <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ChatCompletionMessageToolCall</span><span style=\"font-weight: bold\">(</span>\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'call_11LNwPCU7WRffzUueeDePhun'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">function</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">Function</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">arguments</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'{\"latitude\":52.52,\"longitude\":13.405}'</span>, <span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span><span style=\"font-weight: bold\">)</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">type</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>\n",
              "    <span style=\"font-weight: bold\">)</span>\n",
              "<span style=\"font-weight: bold\">]</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Second Step where we are calling the `get_weather` function and sending the response back to Chat API"
      ],
      "metadata": {
        "id": "xvVxw5mGXWJ6"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "if response.choices[0].finish_reason == \"tool_calls\": # Check if finish_reason is tool_calls\n",
        "    tool_call = response.choices[0].message.tool_calls[0]\n",
        "    arguments = json.loads(tool_call.function.arguments)\n",
        "    latitude = arguments.get(\"latitude\")\n",
        "    longitude = arguments.get(\"longitude\")\n",
        "    # weather = get_weather(latitude, longitude) # Both will work\n",
        "    weather = get_weather(**arguments)\n",
        "    new_message = {\n",
        "        \"role\": \"tool\",\n",
        "        \"content\": json.dumps({\"latitude\": latitude, \"longitude\": longitude, \"weather\": weather}),\n",
        "        \"tool_call_id\": tool_call.id\n",
        "    }\n",
        "    # Important: we will append the previous message (response.choices[0].message)\n",
        "    messages.append(response.choices[0].message)\n",
        "    messages.append(new_message)\n",
        "    response2 = openai.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
        "    print(\"Model Response2 = \",response2.choices[0].message.content)\n",
        "    print(\"Finish Reason = \",response2.choices[0].finish_reason)"
      ],
      "metadata": {
        "id": "WItchxDLK2x6",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "b4214b95-25d7-4ef8-84de-e3189b1d0fdb"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "get_weather function called to get weather for latitude = 52.52, longitude = 13.405\n",
            "And result is  = 8.0\n",
            "Model Response2 =  The current weather in Berlin is characterized by a temperature of approximately 8°C. If you need more specific details such as conditions, humidity, or forecasts, feel free to ask!\n",
            "Finish Reason =  stop\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Responses API\n",
        "\n",
        "https://platform.openai.com/docs/guides/function-calling?api-mode=responses"
      ],
      "metadata": {
        "id": "tCzSosP5K-JR"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "rich.print(pydantic_function_tool(GetWeather))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 289
        },
        "id": "tCSmj7fRaItb",
        "outputId": "185e51ea-7a30-4d1c-9a81-bb3ebd9fcd2d"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m{\u001b[0m\n",
              "    \u001b[32m'type'\u001b[0m: \u001b[32m'function'\u001b[0m,\n",
              "    \u001b[32m'function'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "        \u001b[32m'name'\u001b[0m: \u001b[32m'GetWeather'\u001b[0m,\n",
              "        \u001b[32m'strict'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n",
              "        \u001b[32m'parameters'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "            \u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\n",
              "                \u001b[32m'latitude'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Latitude of the location'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Latitude'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'number'\u001b[0m\u001b[1m}\u001b[0m,\n",
              "                \u001b[32m'longitude'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'description'\u001b[0m: \u001b[32m'Longitude of the location'\u001b[0m, \u001b[32m'title'\u001b[0m: \u001b[32m'Longitude'\u001b[0m, \u001b[32m'type'\u001b[0m: \u001b[32m'number'\u001b[0m\u001b[1m}\u001b[0m\n",
              "            \u001b[1m}\u001b[0m,\n",
              "            \u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'latitude'\u001b[0m, \u001b[32m'longitude'\u001b[0m\u001b[1m]\u001b[0m,\n",
              "            \u001b[32m'title'\u001b[0m: \u001b[32m'GetWeather'\u001b[0m,\n",
              "            \u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n",
              "            \u001b[32m'additionalProperties'\u001b[0m: \u001b[3;91mFalse\u001b[0m\n",
              "        \u001b[1m}\u001b[0m\n",
              "    \u001b[1m}\u001b[0m\n",
              "\u001b[1m}\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span>\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>,\n",
              "    <span style=\"color: #008000; text-decoration-color: #008000\">'function'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'name'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'strict'</span>: <span style=\"color: #00ff00; text-decoration-color: #00ff00; font-style: italic\">True</span>,\n",
              "        <span style=\"color: #008000; text-decoration-color: #008000\">'parameters'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'properties'</span>: <span style=\"font-weight: bold\">{</span>\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'latitude'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Latitude of the location'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Latitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'number'</span><span style=\"font-weight: bold\">}</span>,\n",
              "                <span style=\"color: #008000; text-decoration-color: #008000\">'longitude'</span>: <span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'description'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Longitude of the location'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'Longitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'number'</span><span style=\"font-weight: bold\">}</span>\n",
              "            <span style=\"font-weight: bold\">}</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'required'</span>: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'latitude'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'longitude'</span><span style=\"font-weight: bold\">]</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'title'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'type'</span>: <span style=\"color: #008000; text-decoration-color: #008000\">'object'</span>,\n",
              "            <span style=\"color: #008000; text-decoration-color: #008000\">'additionalProperties'</span>: <span style=\"color: #ff0000; text-decoration-color: #ff0000; font-style: italic\">False</span>\n",
              "        <span style=\"font-weight: bold\">}</span>\n",
              "    <span style=\"font-weight: bold\">}</span>\n",
              "<span style=\"font-weight: bold\">}</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "To use pydantic-generated function, we need to use `openai.responses.parse()` function call\n",
        "\n",
        "https://github.com/openai/openai-python/blob/main/examples/responses/structured_outputs_tools.py"
      ],
      "metadata": {
        "id": "X__yLWt7aIOU"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Using old way of sending history messages in every call"
      ],
      "metadata": {
        "id": "j6VEHNoPaSI1"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "First Step where model will responed with tool call request"
      ],
      "metadata": {
        "id": "Q4E9IoxrTzFV"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "messages=[\n",
        "    {\"role\": \"developer\", \"content\": \"You are a helpful assistant and provide update on weather in a city.\"},\n",
        "    # {\"role\": \"user\", \"content\": \"What's the weather like in Karachi, Pakistan?\"}\n",
        "    # {\"role\": \"user\", \"content\": \"NYC\"}\n",
        "    {\"role\": \"user\", \"content\": \"Berlin\"}\n",
        "]\n",
        "tools = [pydantic_function_tool(GetWeather)]\n",
        "\n",
        "# Note the parse function\n",
        "response = openai.responses.parse(\n",
        "    model=MODEL,\n",
        "    input=messages,\n",
        "    tools = tools\n",
        ")\n",
        "\n",
        "print(\"Status = \",response.status) # Status will not indicate the tool call\n",
        "print(response.output_text) # Empty\n",
        "rich.print(response.output)\n",
        "rich.print(response.output[0].parsed_arguments)"
      ],
      "metadata": {
        "id": "2IPLSF30K--S",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 244
        },
        "outputId": "b3f13b75-cc8c-4048-bd01-cac393605cb5"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Status =  completed\n",
            "\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m[\u001b[0m\n",
              "    \u001b[1;35mParsedResponseFunctionToolCall\u001b[0m\u001b[1m(\u001b[0m\n",
              "        \u001b[33marguments\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"latitude\":52.52,\"longitude\":13.405\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n",
              "        \u001b[33mcall_id\u001b[0m=\u001b[32m'call_99I60SxtFSLNXMfbjZqdwDi8'\u001b[0m,\n",
              "        \u001b[33mname\u001b[0m=\u001b[32m'GetWeather'\u001b[0m,\n",
              "        \u001b[33mtype\u001b[0m=\u001b[32m'function_call'\u001b[0m,\n",
              "        \u001b[33mid\u001b[0m=\u001b[32m'fc_67e2ebe9378881929d2e3a1e4e45af510fb207a87bac8ba8'\u001b[0m,\n",
              "        \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n",
              "        \u001b[33mparsed_arguments\u001b[0m=\u001b[1;35mGetWeather\u001b[0m\u001b[1m(\u001b[0m\u001b[33mlatitude\u001b[0m=\u001b[1;36m52\u001b[0m\u001b[1;36m.52\u001b[0m, \u001b[33mlongitude\u001b[0m=\u001b[1;36m13\u001b[0m\u001b[1;36m.405\u001b[0m\u001b[1m)\u001b[0m\n",
              "    \u001b[1m)\u001b[0m\n",
              "\u001b[1m]\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">[</span>\n",
              "    <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ParsedResponseFunctionToolCall</span><span style=\"font-weight: bold\">(</span>\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">arguments</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'{\"latitude\":52.52,\"longitude\":13.405}'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">call_id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'call_99I60SxtFSLNXMfbjZqdwDi8'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">type</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'function_call'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'fc_67e2ebe9378881929d2e3a1e4e45af510fb207a87bac8ba8'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">status</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'completed'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">parsed_arguments</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">GetWeather</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">latitude</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">52.52</span>, <span style=\"color: #808000; text-decoration-color: #808000\">longitude</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">13.405</span><span style=\"font-weight: bold\">)</span>\n",
              "    <span style=\"font-weight: bold\">)</span>\n",
              "<span style=\"font-weight: bold\">]</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1;35mGetWeather\u001b[0m\u001b[1m(\u001b[0m\u001b[33mlatitude\u001b[0m=\u001b[1;36m52\u001b[0m\u001b[1;36m.52\u001b[0m, \u001b[33mlongitude\u001b[0m=\u001b[1;36m13\u001b[0m\u001b[1;36m.405\u001b[0m\u001b[1m)\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">GetWeather</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">latitude</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">52.52</span>, <span style=\"color: #808000; text-decoration-color: #808000\">longitude</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">13.405</span><span style=\"font-weight: bold\">)</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Second Step where we are calling the `get_weather` function and sending the response back to Responses API"
      ],
      "metadata": {
        "id": "-EJUa5FWafKE"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "To send the result of function call we need to send specific format object into Responses API call\n",
        "\n",
        "Note: object has different property names.\n",
        "\n",
        "```\n",
        "{\n",
        "    \"type\": \"function_call_output\",\n",
        "    \"call_id\": tool_call.call_id,\n",
        "    \"output\": <output of function call>,\n",
        "}\n",
        "```"
      ],
      "metadata": {
        "id": "ihOwJk03aqR-"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "if response.output[0].type == \"function_call\": # Check if output type is function_call\n",
        "    tool_call = response.output[0]\n",
        "    # arguments = json.loads(tool_call.arguments) # Not needed but still works\n",
        "    # latitude = arguments.get(\"latitude\")\n",
        "    # longitude = arguments.get(\"longitude\")\n",
        "    # weather = get_weather(**arguments) # Not needed but still works\n",
        "\n",
        "    # As we are receiving parsed_arguments as object, we can call properties on parsed_arguments object\n",
        "    weather = get_weather(tool_call.parsed_arguments.latitude, tool_call.parsed_arguments.longitude) # Both will work\n",
        "\n",
        "    new_message = {\n",
        "        \"type\": \"function_call_output\",\n",
        "        \"call_id\": tool_call.call_id,\n",
        "        \"output\": str(weather)\n",
        "        # Because of json object in output Responses API sometimes does not generate expected output\n",
        "        # \"output\":  json.dumps({\"latitude\": latitude, \"longitude\": longitude, \"weather\": weather}),\n",
        "    }\n",
        "    # To call use Responses API with history messages we need to send output back but\n",
        "    # it gives an error if we append tool call with parsed_arguments\n",
        "    del response.output[0].parsed_arguments\n",
        "    # Important: we will append the tool call (response.output[0])\n",
        "    messages.append(response.output[0])\n",
        "    messages.append(new_message)\n",
        "    # rich.print(messages)\n",
        "    # Calling the Responses API again with all the history messages and the new message\n",
        "    response2 = openai.responses.parse(model=MODEL, input=messages,tools = tools)\n",
        "    print(\"Model Response2 = \",response2.output_text)\n",
        "    print(\"Status = \",response2.status)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "fP7XJLvCayCL",
        "outputId": "00cdba9d-4f07-473a-bb7b-482c30193d5f"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "get_weather function called to get weather for latitude = 52.52, longitude = 13.405\n",
            "And result is  = 7.8\n",
            "Model Response2 =  The current temperature in Berlin is approximately 7.8°C. If you need more weather details or forecasts, feel free to ask!\n",
            "Status =  completed\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Using new way of conversation state by sending perivous reponse id"
      ],
      "metadata": {
        "id": "1kt-4nnsa089"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "First Step where model will responed with tool call request"
      ],
      "metadata": {
        "id": "wWo6BoJfa3hZ"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# This section is same as above\n",
        "\n",
        "messages=[\n",
        "    {\"role\": \"developer\", \"content\": \"You are a helpful assistant and provide update on weather in a city.\"},\n",
        "    # {\"role\": \"user\", \"content\": \"What's the weather like in Karachi, Pakistan?\"}\n",
        "    # {\"role\": \"user\", \"content\": \"NYC\"}\n",
        "    {\"role\": \"user\", \"content\": \"Berlin\"}\n",
        "]\n",
        "tools = [pydantic_function_tool(GetWeather)]\n",
        "\n",
        "response = openai.responses.parse(\n",
        "    model=MODEL,\n",
        "    input=messages,\n",
        "    tools = tools\n",
        ")\n",
        "\n",
        "print(\"Status = \",response.status) # Status will not indicate the tool call\n",
        "print(response.output_text) # Empty\n",
        "rich.print(response.output)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 228
        },
        "id": "ePKGsA50a1Qx",
        "outputId": "83393946-84c1-4262-c30a-ee1094e4dfea"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Status =  completed\n",
            "\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "\u001b[1m[\u001b[0m\n",
              "    \u001b[1;35mParsedResponseFunctionToolCall\u001b[0m\u001b[1m(\u001b[0m\n",
              "        \u001b[33marguments\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"latitude\":52.52,\"longitude\":13.405\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n",
              "        \u001b[33mcall_id\u001b[0m=\u001b[32m'call_La0TfL9JSyiKm2ulR4TrpTfk'\u001b[0m,\n",
              "        \u001b[33mname\u001b[0m=\u001b[32m'GetWeather'\u001b[0m,\n",
              "        \u001b[33mtype\u001b[0m=\u001b[32m'function_call'\u001b[0m,\n",
              "        \u001b[33mid\u001b[0m=\u001b[32m'fc_67e2ec7418208192a4e6a39a8a8e93790407d7d934461a05'\u001b[0m,\n",
              "        \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n",
              "        \u001b[33mparsed_arguments\u001b[0m=\u001b[1;35mGetWeather\u001b[0m\u001b[1m(\u001b[0m\u001b[33mlatitude\u001b[0m=\u001b[1;36m52\u001b[0m\u001b[1;36m.52\u001b[0m, \u001b[33mlongitude\u001b[0m=\u001b[1;36m13\u001b[0m\u001b[1;36m.405\u001b[0m\u001b[1m)\u001b[0m\n",
              "    \u001b[1m)\u001b[0m\n",
              "\u001b[1m]\u001b[0m\n"
            ],
            "text/html": [
              "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">[</span>\n",
              "    <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">ParsedResponseFunctionToolCall</span><span style=\"font-weight: bold\">(</span>\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">arguments</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'{\"latitude\":52.52,\"longitude\":13.405}'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">call_id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'call_La0TfL9JSyiKm2ulR4TrpTfk'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">name</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'GetWeather'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">type</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'function_call'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">id</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'fc_67e2ec7418208192a4e6a39a8a8e93790407d7d934461a05'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">status</span>=<span style=\"color: #008000; text-decoration-color: #008000\">'completed'</span>,\n",
              "        <span style=\"color: #808000; text-decoration-color: #808000\">parsed_arguments</span>=<span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">GetWeather</span><span style=\"font-weight: bold\">(</span><span style=\"color: #808000; text-decoration-color: #808000\">latitude</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">52.52</span>, <span style=\"color: #808000; text-decoration-color: #808000\">longitude</span>=<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">13.405</span><span style=\"font-weight: bold\">)</span>\n",
              "    <span style=\"font-weight: bold\">)</span>\n",
              "<span style=\"font-weight: bold\">]</span>\n",
              "</pre>\n"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Second Step where we are calling the `get_weather` function and sending the response back to Responses API"
      ],
      "metadata": {
        "id": "6uIbmV86a8Wa"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "The only difference in below section is how messages are sent."
      ],
      "metadata": {
        "id": "b5BrKSX9a-r3"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# The only difference in this section is how messages are sent.\n",
        "\n",
        "if response.output[0].type == \"function_call\": # Check if output type is function_call\n",
        "    tool_call = response.output[0]\n",
        "    # arguments = json.loads(tool_call.arguments) # Not needed but still works\n",
        "    # latitude = arguments.get(\"latitude\")\n",
        "    # longitude = arguments.get(\"longitude\")\n",
        "    # weather = get_weather(**arguments) # Not needed but still works\n",
        "\n",
        "    weather = get_weather(tool_call.parsed_arguments.latitude, tool_call.parsed_arguments.longitude) # Both will work\n",
        "\n",
        "    new_message = {\n",
        "        \"type\": \"function_call_output\",\n",
        "        \"call_id\": tool_call.call_id,\n",
        "        \"output\": str(weather)\n",
        "    }\n",
        "    # Not needed now because we are using response.id\n",
        "    # del response.output[0].parsed_arguments\n",
        "\n",
        "    # Not needed now because we are using response.id\n",
        "    # messages.append(response.output[0])\n",
        "\n",
        "    # Emptying the messages array because we are sending the previous response id,\n",
        "    # therefore we don't need to send the previous message\n",
        "    messages = []\n",
        "    messages.append(new_message)\n",
        "    response2 = openai.responses.parse(model=MODEL, input=messages,tools = tools, previous_response_id=response.id)\n",
        "    print(\"Model Response2 = \",response2.output_text)\n",
        "    print(\"Status = \",response2.status)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "nAif9Tq6a8_B",
        "outputId": "864efc17-8a3f-4e2a-c8b2-313b4a1198d8"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "get_weather function called to get weather for latitude = 52.52, longitude = 13.405\n",
            "And result is  = 7.8\n",
            "Model Response2 =  The current temperature in Berlin is approximately 7.8°C. If you need more detailed weather information, let me know!\n",
            "Status =  completed\n"
          ]
        }
      ]
    }
  ]
}